﻿using System;
using System.Linq;
using System.Collections.Generic;

namespace Aeon.Emulator.Decoding.Recompiler
{
    /// <summary>
    /// Implements an x86 dynamic recompiler.
    /// </summary>
    public sealed class DynamicRecompiler
    {
        private readonly VirtualMachine vm;
        private Dictionary<BlockAddress, CompiledBlock> cache;
        private Compiler recompiler;

        /// <summary>
        /// Initializes a new instance of the DynamicRecompiler class.
        /// </summary>
        /// <param name="vm">Associated VirtualMachine instance.</param>
        public DynamicRecompiler(VirtualMachine vm)
        {
            if(vm == null)
                throw new ArgumentNullException("vm");

            this.vm = vm;
            this.cache = new Dictionary<BlockAddress, CompiledBlock>();
            this.recompiler = new Compiler(vm);
        }

        /// <summary>
        /// Clears the compiler cache, forcing all subsequent calls to recompile.
        /// </summary>
        public void ClearCache()
        {
            this.cache = new Dictionary<BlockAddress, CompiledBlock>();
        }
        /// <summary>
        /// Runs the next block of instructions starting at CS:EIP.
        /// </summary>
        /// <returns>Number of instructions run.</returns>
        public int Run()
        {
            CompiledBlock block;
            var address = new BlockAddress(vm.Processor.CS, vm.Processor.EIP);

            if(this.cache.TryGetValue(address, out block))
                return block.Run(vm);

            block = this.recompiler.Compile(vm.Processor.CS, vm.Processor.EIP, 500);
            this.cache[address] = block;
            return block.Run(vm);
        }
    }
}
